home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / Publishing / ImagePortfolio / Source / GifImageRep.m < prev    next >
Text File  |  1994-04-01  |  12KB  |  453 lines

  1. // -------------------------------------------------------------------------------------
  2. // GifImageRep.m
  3. // Martin D. Flynn, NeXT Computer, Inc.
  4. // You may freely copy, distribute and reuse the code in this example.
  5. // NeXT disclaims any warranty of any kind, expressed or implied, as to its
  6. // fitness for any particular use.
  7. //
  8. // Note: Much of the source in this object has been a heavy modification of various
  9. //       public domain sources for GIF file conversion programs.
  10. // -------------------------------------------------------------------------------------
  11.  
  12. #import <stdlib.h>
  13. #import <stdio.h>
  14. #import <string.h>
  15. #import <libc.h>
  16. #import <objc/objc.h>
  17. #import <dpsclient/dpsclient.h>
  18. #import <defaults/defaults.h>
  19. #import <objc/List.h>
  20. #import <appkit/tiff.h>
  21. #import <appkit/NXImage.h>
  22. #import <appkit/Application.h>
  23. #import <appkit/nextstd.h>
  24. #import "GifImageRep.h"
  25.  
  26. // -------------------------------------------------------------------------------------
  27. // error codes & descriptions
  28. #define        errNONE                    0
  29. #define        errTABLE_OVERFLOW        1
  30. #define        errRASTER_SIZE            2
  31. #define        errNO_COLORMAP            3
  32. #define        errINVALID_FILE            4
  33. #define        errBAD_BLOCK            5
  34. #define        errCANT_LOAD            6
  35. #define        errEND_OF_FILE            7
  36. #define        errNO_IMAGE                8
  37. static char *errDesc[] = {
  38.             "none",
  39.             "code table overflow",
  40.             "raster has the wrong size",
  41.             "no colormap present for image",
  42.             "invalid GIF file",
  43.             "illegal GIF block type",
  44.             "unable to load file (unknown reason)",
  45.             "reached premature end-of-file"
  46.             "no image found in file",
  47.             0
  48. };
  49. const char *errorDesc(int x) { return errDesc[x]; }
  50.  
  51. // -------------------------------------------------------------------------------------
  52. @implementation GifImageRep
  53.  
  54. // -------------------------------------------------------------------------------------
  55. // internal support methods
  56. // -------------------------------------------------------------------------------------
  57.  
  58. /* decode a raster image */
  59. // Note: A 'goto'? Yuck! I know their ugly, but I just haven't remove it yet. sorry.
  60. - (int)_readRaster
  61. {
  62.     u_char        *ch, buf[260], *fill;
  63.     u_int        count, datm;
  64.     int            i, err, code, bits, thisCode;
  65.     int            codeTable[4096][2];    // LZW compression code data
  66.     int            stack[4096 * 2], *sp;
  67.     int            _datasize, _codesize, _maxsize;
  68.     int            _clear, _avail, _oldcode, _firstcode;
  69.  
  70.     /* initialize */
  71.     err        = errNONE;
  72.     fill       = imageRaster;
  73.     _datasize  = getc(imageStream);
  74.     _clear     = 1 << _datasize;
  75.     _codesize  = _datasize + 1;
  76.     _avail     = _clear + 2;
  77.     _maxsize   = _clear * 2;
  78.     _oldcode   = -1;
  79.     _firstcode = -1;
  80.     bits       = 0;
  81.     datm       = 0;
  82.     sp         = stack;
  83.  
  84.     /* prefill table */
  85.     for (i = 0; i < 4096; i++) {
  86.         codeTable[i][0] = 0;
  87.         codeTable[i][1] = (i < _clear)? i : 0;
  88.     }
  89.   
  90.     /* decode raster */
  91.     for (count = getc(imageStream); count > 0; count = getc(imageStream)) {
  92.   
  93.         /* read block */
  94.         if (fread(buf, 1, count, imageStream) != count) { err = errEND_OF_FILE; break; }
  95.     
  96.         /* process block */
  97.         for (ch = buf; count-- > 0; ch++) {
  98.             datm += (u_int)(*ch) << bits;
  99.             bits += 8;
  100.             while (bits >= _codesize) {
  101.       
  102.                 /* get code */
  103.                 code = datm & ((1 << _codesize) - 1);
  104.                 datm >>= _codesize;
  105.                 bits -= _codesize;
  106.  
  107.                 /* clear table */
  108.                 if (code == _clear) {
  109.                     _codesize  = _datasize + 1;
  110.                     _maxsize   = _clear * 2;
  111.                     _avail     = _clear + 2;
  112.                     _oldcode   = -1;
  113.                     _firstcode = -1;
  114.                     sp         = stack;
  115.                     for (i = 0; i < 4096; i++) {
  116.                         codeTable[i][0] = 0;
  117.                         codeTable[i][1] = (i < _clear)? i : 0;
  118.                     }
  119.                     continue;
  120.                 }
  121.         
  122.                 /* end of decompression */
  123.                 if (code == _clear + 1) goto exitloop;  /* for non-standard GIF files */
  124.  
  125.                 /* save first code */
  126.                 if (_firstcode == -1) {
  127.                     _firstcode = _oldcode = code;
  128.                     *fill++ = code;
  129.                     continue;
  130.                 }
  131.  
  132.                 /* save this code */
  133.                 thisCode = code;
  134.  
  135.                 /* code check */
  136.                 if (code >= _avail) {
  137.                     *sp++ = _firstcode;
  138.                     code = _oldcode;
  139.                 }
  140.  
  141.                 /* place codes on stack */
  142.                 while (code >= _clear) {
  143.                     *sp++ = codeTable[code][1];
  144.                     if (code == codeTable[code][0]) {
  145.                         err = errTABLE_OVERFLOW;
  146.                         goto exitloop;
  147.                     }
  148.                     code = codeTable[code][0];
  149.                 }
  150.                 *sp++ = _firstcode = codeTable[code][1];
  151.  
  152.                 /* save in table */
  153.                 code = _avail;
  154.                 if (code < 4096) {
  155.                     codeTable[code][0] = _oldcode;
  156.                     codeTable[code][1] = _firstcode;
  157.                     _avail++;
  158.                     if ((_avail >= _maxsize) && (_maxsize < 4096)) {
  159.                         _maxsize *= 2;
  160.                         _codesize++;
  161.                     }
  162.                 }
  163.  
  164.                 /* save old code */
  165.                 _oldcode = thisCode;
  166.         
  167.                 /* flush stack */
  168.                 while (sp > stack) *fill++ = *--sp;
  169.         
  170.             }
  171.         }
  172.     
  173.     }
  174.   
  175. exitloop:
  176.     if (err == errEND_OF_FILE) {
  177.         err = errNONE;
  178.         NXLogError("GIF premature end of file");
  179.     }
  180.     if (!err && (fill != imageRaster + rasterSize)) err = errRASTER_SIZE;
  181.     return err;
  182.  
  183. }
  184.  
  185. /* load image raster */
  186. - (int)_loadImage
  187. {
  188.     u_char        buf[10];
  189.     BOOL        local, interleaved;
  190.     int            err, left;
  191.  
  192.     /* set file position */
  193.     if (imagePos) fseek(imageStream, imagePos, SEEK_SET);
  194.     else imagePos = ftell(imageStream);
  195.   
  196.     /* read image information */
  197.     fread(buf, 1, 9, imageStream);
  198.     left        = buf[0] + (buf[1] << 8);
  199.     rasterTop   = buf[2] + (buf[3] << 8);
  200.     pixWide     = buf[4] + (buf[5] << 8);
  201.     pixHigh     = buf[6] + (buf[7] << 8);
  202.     local       = (buf[8] & 0x80)? YES : NO;
  203.     interleaved = (buf[8] & 0x40)? YES : NO;
  204.     rasterSize  = (u_long)pixWide * (u_long)pixHigh;
  205.  
  206.     /* convert color map */
  207.     if (local) {
  208.         colorMapCount = 1 << ((buf[8] & 0x7) + 1);
  209.         fread(colorMap, 3, colorMapCount, imageStream);
  210.     } else
  211.     if (!isGlobalMap) return errNO_COLORMAP;
  212.  
  213.     /* read image raster */
  214.     if (!imageRaster) memset((imageRaster=(u_char*)malloc(rasterSize)), 0, rasterSize);
  215.     if ((err = [self _readRaster]) && (err != errRASTER_SIZE)) return err;
  216.  
  217.     /* remove any interleave */
  218.     if (interleaved) {
  219.         int        i, r = 0;
  220.         u_char    *temp = (u_char*)malloc(rasterSize);
  221.         u_short    *tbl = (u_short*)malloc(pixHigh * sizeof(u_short));
  222.         for (i = rasterTop    ; i < rasterTop + pixHigh; i += 8) tbl[i] = r++;
  223.         for (i = rasterTop + 4; i < rasterTop + pixHigh; i += 8) tbl[i] = r++;
  224.         for (i = rasterTop + 2; i < rasterTop + pixHigh; i += 4) tbl[i] = r++;
  225.         for (i = rasterTop + 1; i < rasterTop + pixHigh; i += 2) tbl[i] = r++;
  226.         for (i = 0, r = rasterTop; r < rasterTop + pixHigh; i++, r++)
  227.             memcpy(&temp[i * pixWide], &imageRaster[tbl[r] * pixWide], pixWide);
  228.         free(tbl);
  229.         free(imageRaster);
  230.         imageRaster = temp;
  231.         rasterTop = 0;
  232.     }
  233.  
  234.     /* return successful */
  235.     return errNONE;
  236.   
  237. }
  238.  
  239. /* read GIF header info */
  240. - (int)_readGIFHeader
  241. {
  242.     u_int    screenWidth, screenHeight;                                // screen dimensions
  243.     u_int    background;
  244.     u_char    buf[256];
  245.  
  246.     /* rewind file pointer */
  247.     rewind(imageStream);
  248.   
  249.     /* check gif signiture type */
  250.     if (![[self class] validImageType:imageStream]) return errINVALID_FILE;
  251.     
  252.     /* read global information */
  253.     fread(buf, 1, 7, imageStream);
  254.     screenWidth  = buf[0] + (buf[1] << 8);                            // not used
  255.     screenHeight = buf[2] + (buf[3] << 8);                            // not used
  256.     isGlobalMap  = (buf[4] & 0x80)? YES : NO;                        // global color map?
  257.     background   = buf[5];                                            // not used
  258.     aspectRatio  = (buf[6])? ((float)buf[6] + 15.0) / 64.0 : 1.0;    // aspect ratio
  259.  
  260.     /* load global colormap */
  261.     if (isGlobalMap) {
  262.         colorMapCount = 1 << ((buf[4] & 0x07) + 1);
  263.         fread(colorMap, 3, colorMapCount, imageStream);
  264.     }
  265.   
  266.     return errNONE;
  267. }
  268.  
  269. /* initialize from file */
  270. - (int)_readImageAtPosition:(long int)overridePos
  271. {
  272.     int    err;
  273.  
  274.     /* read gif header info */
  275.     if (err = [self _readGIFHeader]) return err;
  276.  
  277.     /* set file position override */
  278.     if (overridePos) fseek(imageStream, overridePos, SEEK_SET);
  279.   
  280.     /* loop through file */
  281.     for(;;) {
  282.         int err;
  283.         u_char buf[256], ch = getc(imageStream);
  284.         switch (ch) {
  285.             case ',' :
  286.                 if (err = [self _loadImage]) return err;
  287.                 imageNextPos = ftell(imageStream);
  288.                 return errNONE;
  289.             case '!' :    // skip extension
  290.                 for (ch=getc(imageStream); ch=getc(imageStream);)
  291.                     fread(buf, 1, ch, imageStream);
  292.                 break;
  293.             case '\0':
  294.                 break;    /* non-standard files */
  295.             case ';' :
  296.                 return imageNextPos? errNONE: errNO_IMAGE;
  297.             default  :
  298.                 return errBAD_BLOCK;
  299.         }
  300.     }
  301.  
  302.     /* return any error */
  303.     return errNONE;
  304.   
  305. }
  306.  
  307. // -------------------------------------------------------------------------------------
  308. // internal init method
  309. // -------------------------------------------------------------------------------------
  310.  
  311. /* init from file stream */
  312. - _initFromFile:(const char*)filename atPos:(long int)overridePos
  313. {
  314.     NXSize    pSize;
  315.   
  316.     /* generic initialization */
  317.     [self initDrawMethod:@selector(_drawImage:) inObject:self];
  318.   
  319.     /* local var init */
  320.     imageRaster        = (u_char*)nil;
  321.     pixWide          = 0;
  322.     pixHigh         = 0;
  323.     rasterSize        = 0L;
  324.     isGlobalMap        = NO;
  325.     colorMapCount    = 0;
  326.     imagePos        = 0L;
  327.     imageNextPos    = 0L;
  328.   
  329.     /* verify stream */
  330.     imageStream = fopen(filename, "r");
  331.     if (!imageStream) { [self free]; return (id)nil; }
  332.   
  333.     /* initialize */
  334.     loadError = [self _readImageAtPosition:overridePos];
  335.     if (loadError == errNO_IMAGE) { [self free]; return (id)nil; }
  336.     if (!loadError && (!pixWide || !pixHigh)) loadError = errCANT_LOAD;
  337.     if (loadError) {
  338.         NXLogError("GIF stream error: %s", errDesc[loadError]);
  339.         [self free];
  340.         return (id)nil;
  341.     }
  342.  
  343.     /* init size (check aspect ratio) */
  344.     pSize.width  = (float)pixWide;
  345.     pSize.height = (float)pixHigh;
  346.     if (aspectRatio < 1.0) pSize.width  *= 1.0 / aspectRatio; else
  347.     if (aspectRatio > 1.0) pSize.height *= aspectRatio;
  348.  
  349.     /* init custom image rep */
  350.     [self setSize:&pSize];
  351.     [self setPixelsWide:pixWide];
  352.     [self setPixelsHigh:pixHigh];
  353.     [self setBitsPerSample:8];
  354.     [self setAlpha:NO];
  355.     [self setNumColors:256];
  356.  
  357.     return self;
  358. }
  359.  
  360. // -------------------------------------------------------------------------------------
  361. // public advertised methods
  362. // -------------------------------------------------------------------------------------
  363.  
  364. /* return true if file is gif image (only check file extension) */
  365. + (BOOL)validImageFile:(const char*)filename
  366. {
  367.     char    *extn = rindex((char*)filename, '.');
  368.     return (extn && (!strcmp(extn, ".gif") || !strcmp(extn, ".GIF")))? YES : NO;
  369. }
  370.  
  371. /* check file type */
  372. + (BOOL)validImageType:(FILE*)fd
  373. {
  374.     char    buf[16];
  375.     fread(buf, 1, 6, fd);
  376.     return (strncmp(buf, "GIF87a", 6) && strncmp(buf, "GIF89a", 6))? NO : YES;
  377. }
  378.  
  379. /* read all images into a list */
  380. + (List*)newListFromFile:(const char*)filename
  381. {
  382.     id            list, _class = self;
  383.     long int    pos;
  384.     
  385.     /* verify file */
  386.     if (![self validImageFile:filename]) return (id)nil;
  387.  
  388.     /* start loading images */
  389.     list = [[[List alloc] initCount:1] empty];
  390.     for (pos = 0L;;) {
  391.         if (!(self = [[_class alloc] _initFromFile:filename atPos:pos])) break;
  392.         [list addObject:self];
  393.         pos = imageNextPos;
  394.     }
  395.     
  396.     /* return list */
  397.     if (![list count]) { [list free]; list = (id)nil; }
  398.     return list;
  399.     
  400. }
  401.  
  402. /* init from file stream */
  403. - initFromFile:(const char*)filename
  404. {
  405.     if (![[self class] validImageFile:filename]) { [self free]; return (id)nil; }
  406.     return [self _initFromFile:filename atPos:0L];
  407. }
  408.  
  409. /* free gif */
  410. - free
  411. {
  412.     if (imageRaster) free(imageRaster);
  413.     if (imageStream) fclose(imageStream);
  414.     return [super free];
  415. }
  416.  
  417. // -------------------------------------------------------------------------------------
  418. // custom image draw
  419. // -------------------------------------------------------------------------------------
  420.  
  421. /* draw image */
  422. - _drawImage:mySelf
  423. {
  424.     int                r, c, bpr;
  425.     u_char            *p;
  426.     NXRect            rect = { { 0.0, 0.0 }, { 0.0, 0.0 } };
  427.     const u_char    *bitmap[5] = { 0, 0, 0, 0, 0 };
  428.  
  429.     /* make sure the image raster is loaded */
  430.     if (loadError || (!imageRaster && (loadError=[self _loadImage]))) return self;
  431.  
  432.     /* build image bitmap */
  433.     bitmap[0] = p = (u_char*)malloc(3L * rasterSize);
  434.     for (r = rasterTop; r < rasterTop + pixHigh; r++) {
  435.         u_char *s = &imageRaster[r * pixWide];
  436.         for (c = 0; c < pixWide; c++, p+=3) memcpy(p, colorMap[s[c]], 3);
  437.     }
  438.  
  439.     /* render bitmap */
  440.     rect.size = size;
  441.     bpr = (7 + pixWide * 8 * 3) / 8;
  442.     NXDrawBitmap(&rect, pixWide, pixHigh, 8, 3, 8*3, bpr, NO, NO, NX_RGBColorSpace, bitmap);
  443.  
  444.     /* free bitmap storage */
  445.     free((char*)bitmap[0]);
  446.  
  447.     /* return successful */
  448.     return self;
  449.   
  450. }
  451.  
  452. @end
  453.